﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Lucene.Net.Analyzers.IFilter;
using System.IO;

using Lucene.Net.Search;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Analysis;
using Lucene.Net.Analyzers.IFilter;

namespace FileIndexer
{
    public partial class Form1 : Form
    {
        string _PathsFile = null;
        public string PathsFile
        {
            get
            {
                if (_PathsFile != null) return _PathsFile;
                _PathsFile = System.Configuration.ConfigurationSettings.AppSettings["PathsFile"];
                return _PathsFile;

            }
        }
        List<Lucene.Net.Analyzers.IFilter.IndexPath> _indexPaths = null;
        public List<Lucene.Net.Analyzers.IFilter.IndexPath> IndexPaths
        {
            get
            {
                if (_indexPaths != null) return _indexPaths;
                if(!System.IO.File.Exists(PathsFile)) 
                    _indexPaths = new List<Lucene.Net.Analyzers.IFilter.IndexPath>();
                else
                    _indexPaths = (Unified.Serialize.DeserializeXMLFromDisk(PathsFile, typeof(List<Lucene.Net.Analyzers.IFilter.IndexPath>)) as List<Lucene.Net.Analyzers.IFilter.IndexPath>);

                return _indexPaths;
            }
            set
            {
                _indexPaths = value;
                List<IndexPath> trimmedList = new List<IndexPath>();
                foreach(IndexPath p in _indexPaths) {
                    if (p.Path !=null && p.Path.Trim().Length > 0) trimmedList.Add(p);
                }
                _indexPaths = trimmedList;
                Unified.Serialize.SerializeXMLToDisk(_indexPaths, PathsFile);
                UpdatePathsGrid();
            }
        }

        private void UpdatePathsGrid()
        {
            this.IndexPathGrid.DataSource = IndexPaths;
        }

        void IndexPathGrid_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
        {
            this.IndexPathGrid.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
        }
        public Form1()
        {
            InitializeComponent();
            System.Random rnd = new Random();
            //IndexName = string.Format("STORE_{0}", rnd.NextDouble().ToString());
            if (!System.IO.Directory.Exists(IndexName)) System.IO.Directory.CreateDirectory(IndexName);
            if (!System.IO.Directory.Exists(DictName)) System.IO.Directory.CreateDirectory(DictName);
            //Lucene.Net.Store.RAMDirectory dir = new Lucene.Net.Store.RAMDirectory(new Lucene.Net.Store.MMapDirectory(new DirectoryInfo(System.IO.Path.GetFullPath(IndexName))));
            //ram = new Lucene.Net.Store.RAMDirectory(IndexName);
            //ram = new Lucene.Net.Store.RAMDirectory(dir);
            //IndexButton_Click(null, null);           
            this.IndexPathGrid.DataBindingComplete += new DataGridViewBindingCompleteEventHandler(IndexPathGrid_DataBindingComplete);
            updateSuggest = new MethodInvoker(EnableSuggested);
            updateLog = new MethodInvoker(UpdateLog);
            updateSize = new MethodInvoker(UpdateSize);
            UpdatePathsGrid();
        }
        object _SizeLock = new object();
        double storeSize = 0;
        private void UpdateSize()
        {
            lock (_SizeLock)
            {
                storeSize = 0;
                foreach (System.IO.FileInfo fi in storeIndex.GetDirectory().GetFiles("*.*", SearchOption.AllDirectories))
                {
                    storeSize += fi.Length;
                }
                if (storeSize > 0)
                {
                    storeSize = storeSize / 1024 / 1024;
                    this.IndexSizeLabel.Text = string.Format("{0:n} MB", storeSize);
                }
            }
        }
        private void UpdateLog()
        {
            this.progressBar1.Maximum = fileCount;
            if (this.progressBar1.Maximum < count) this.progressBar1.Maximum = count;
            this.progressBar1.Value = count;
            //LogResultsTextbox.Text += logVal;
            LogResultsTextbox.AppendText(logVal);
        }
        int count = 0;
        int fileCount = 0;
        string logVal="";
        MethodInvoker updateLog;
        MethodInvoker updateSuggest;
        MethodInvoker updateSize;  

        public static string IndexName = "STORE";
        public static string DictName = "DICTIONARY";
        Lucene.Net.Store.FSDirectory dictIndex = Lucene.Net.Store.FSDirectory.GetDirectory(DictName);
        Lucene.Net.Store.FSDirectory storeIndex = Lucene.Net.Store.FSDirectory.GetDirectory(IndexName);

        
        Analyzer analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_CURRENT);

        object _IndexWork = new object();
        List<string> badExtensions = new List<string>();
        private void Index(object state)
        {
            lock (_IndexWork)
            {
                bool recreate = (bool)state;

                badExtensions = new List<string>();
                badExtensions.Add(".vsd");
                System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
                sw.Start();
                double totalSize = 0;
                bool create = (recreate || (storeIndex.GetDirectory().GetFiles().Length <= 0));
                Lucene.Net.Index.IndexWriter writer = new Lucene.Net.Index.IndexWriter(storeIndex, analyzer, create, IndexWriter.MaxFieldLength.UNLIMITED);

                logVal += "Initializing Index\r\n";
                this.Invoke(updateLog);
                Application.DoEvents();

                if (this.IndexPaths != null)
                {
                    foreach (Lucene.Net.Analyzers.IFilter.IndexPath path in this.IndexPaths)
                    {
                        count = 0;
                        logVal = string.Format("Index Path:{0}{1}", path.Path, System.Environment.NewLine);
                        this.Invoke(updateLog);
                        Application.DoEvents();
                        logVal = "";
                        if (path.Indexed && System.IO.Directory.Exists(path.Path))
                        {
                            System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(path.Path);
                            System.IO.FileInfo[] files = dir.GetFiles("*.*", SearchOption.AllDirectories);
                            fileCount = files.Length;
                            foreach (System.IO.FileInfo fi in files)
                            {
                                this.Invoke(updateLog);
                                logVal = "";
                                Application.DoEvents();
                                count++;
                                try
                                {
                                    Application.DoEvents();

                                    if (!badExtensions.Contains(fi.Extension.ToLower()))
                                    {

                                        string items;
                                        using (FilterReader rdr = new FilterReader(fi.FullName))
                                        {
                                            if (rdr.Filtered)
                                            {
                                                Lucene.Net.Documents.Document fileDoc = new Lucene.Net.Documents.Document();
                                                items = rdr.ReadToEnd();

                                                fileDoc.Add(new Lucene.Net.Documents.Field("created", rdr.FileInfo.CreationTimeUtc.ToString(), Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED));
                                                fileDoc.Add(new Lucene.Net.Documents.Field("file", rdr.FileInfo.FullName, Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED));
                                                fileDoc.Add(new Lucene.Net.Documents.Field("name", rdr.FileInfo.Name.Replace(rdr.FileInfo.Extension, ""), Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED));
                                                fileDoc.Add(new Lucene.Net.Documents.Field("access", rdr.FileInfo.LastAccessTimeUtc.ToString(), Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED));
                                                fileDoc.Add(new Lucene.Net.Documents.Field("write", rdr.FileInfo.LastWriteTimeUtc.ToString(), Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED));
                                                fileDoc.Add(new Lucene.Net.Documents.Field("length", rdr.FileInfo.Length.ToString(), Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED));
                                                totalSize += rdr.FileInfo.Length;
                                                fileDoc.Add(new Lucene.Net.Documents.Field("contents", string.Format("{0}\r\n{1}", items, rdr.FileInfo.Name), Lucene.Net.Documents.Field.Store.YES, Lucene.Net.Documents.Field.Index.ANALYZED));
                                                writer.AddDocument(fileDoc);
                                                logVal += string.Format("SUCCESS: {0} ({2} of {3}){1}", fi.FullName, System.Environment.NewLine, count, files.Length);
                                            }
                                            else
                                            {
                                                logVal += string.Format("NO FILTER: {0} ({2} of {3}){1}", fi.FullName, System.Environment.NewLine, count, files.Length);
                                                badExtensions.Add(fi.Extension.ToLower());
                                            }
                                        }
                                    }
                                    else
                                    {
                                        logVal += string.Format("SKIPPING: {0} ({2} of {3}){1}", fi.FullName, System.Environment.NewLine, count, files.Length);

                                    }
                                }
                                catch (Exception exc)
                                {
                                    if (((System.Runtime.InteropServices.ExternalException)(exc)).ErrorCode != -2147467259)
                                    {
                                        badExtensions.Add(fi.Extension.ToLower());
                                    }
                                    logVal += "FAIL: " + fi.FullName + exc.ToString() + System.Environment.NewLine;
                                }
                                if (!indexRun)
                                {
                                    StopIndexing();
                                    break;
                                }
                            }
                        }
                    }
                }
                writer.Close();

                logVal += "Finalizing Index\r\n";
                this.Invoke(updateLog);
                Application.DoEvents();

                System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(LoadDictionary), null);

                System.TimeSpan ts = new TimeSpan(sw.ElapsedTicks);
                logVal += string.Format("COMPLETED: {0} seconds to index: {1:n}MB{2}", ts.TotalSeconds, (totalSize / 1024f / 1024f), System.Environment.NewLine + System.Environment.NewLine);
                this.Invoke(updateLog);
                logVal = "";

                this.Invoke(updateSize);
            }
        }
        bool indexRun = false;
        private void IndexButton_Click(object sender, EventArgs e)
        {            
            this.LogResultsTextbox.Text  = "";
            indexRun = true;
            System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(Index), true);
        }
        void EnableSuggested()
        {
            SuggestedTextbox.Enabled = true;
            LogResultsTextbox.Text += "DICTIONARY IS ONLINE"+ System.Environment.NewLine;

        }
        
        void LoadDictionary(object state)
        {            
            spellchecker = new SpellChecker.Net.Search.Spell.SpellChecker(dictIndex);
            Lucene.Net.Index.IndexReader rdr = Lucene.Net.Index.IndexReader.Open(IndexName);
            spellchecker.IndexDictionary(new SpellChecker.Net.Search.Spell.LuceneDictionary(rdr, "contents"));

            more = new Similarity.Net.MoreLikeThis(rdr);
            more.SetFieldNames(new string[] { "file" });

            Invoke(updateSuggest);
        }
        Similarity.Net.MoreLikeThis more;
        SpellChecker.Net.Search.Spell.SpellChecker spellchecker;
        Query query;

        object _SearchLock = new object();
        Lucene.Net.Search.IndexSearcher indexSearcher = null;

        private void Search(string Criteria)
        {
            lock (_SearchLock)
            {
                if (Criteria.Length <= 0) return;
                listView1.Items.Clear();
                if (indexSearcher == null) indexSearcher = new Lucene.Net.Search.IndexSearcher(storeIndex, true);

                

                QueryParser parser = new QueryParser("contents", analyzer);
                query = parser.Parse(Criteria);

                ScoreDoc[] docs = indexSearcher.Search(query, null, 1000).scoreDocs;

                if (docs.Length > 0)
                {

                    foreach (ScoreDoc doc in docs)
                    {
                        Lucene.Net.Documents.Document lDoc = indexSearcher.Doc(doc.doc);
                        ListViewItem item = new ListViewItem();
                        System.IO.FileInfo file = new FileInfo(lDoc.GetField("file").StringValue());
                        item.Text = file.FullName;
                        item.Tag = lDoc;

                        string contents = lDoc.GetField("contents").StringValue();
                        TokenStream ts = analyzer.TokenStream("contents", new StringReader(contents));
                        Lucene.Net.Highlight.Highlighter highlighter = new Lucene.Net.Highlight.Highlighter(new Lucene.Net.Highlight.QueryScorer(query));
                        string fragments = highlighter.GetBestFragments(ts, contents, 1, "...");

                        listView1.Items.Add(item);

                        item.SubItems.Add(fragments);

                        //Query q = more.Like(doc.doc);
                        //ScoreDoc[] moreDocs = src.Search(q, null, 1).scoreDocs;
                    }
                    listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
                }
                else
                {
                    if (SuggestedTextbox.Enabled && spellchecker !=null)
                    {
                        SuggestedTextbox.Text = "";
                        String[] suggestions = spellchecker.SuggestSimilar(Criteria, 5);
                        if (suggestions != null)
                        {
                            foreach (string sug in suggestions)
                            {
                                if (Criteria.ToLower().IndexOf(sug.ToLower()) < 0)
                                {
                                    SuggestedTextbox.Text += string.Format("{0} ", sug);
                                }
                            }
                        }
                    }
                }
            }
        }
        private void SearchButton_Click(object sender, EventArgs e)
        {
            Search(SearchCriteriaTextbox.Text);
        }


        private void listView1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (listView1.SelectedItems.Count > 0)
            {
                ListViewItem item =(listView1.SelectedItems[0] as ListViewItem);
                if (item != null)
                {
                    Lucene.Net.Documents.Document lDoc = (item.Tag as Lucene.Net.Documents.Document);
                    if (lDoc != null)
                    {
                        string contents = lDoc.GetField("contents").StringValue();
                        richTextBox1.Text = contents;
                        

                    }
                }
            }
        }

        private void listView1_DoubleClick(object sender, EventArgs e)
        {
            ListViewItem item = (listView1.SelectedItems[0] as ListViewItem);
            if (item != null)
            {
                Lucene.Net.Documents.Document lDoc = (item.Tag as Lucene.Net.Documents.Document);
                if (lDoc != null)
                {
                    string path = lDoc.GetField("file").StringValue();
                    if (System.IO.File.Exists(path))
                    {
                        if (System.Windows.Forms.MessageBox.Show("Do you want to open the file:" + path, "Confirmation", MessageBoxButtons.YesNo) == DialogResult.Yes)
                        {
                            System.Diagnostics.Process.Start(path);
                        }
                    }
                }
            }
        }

        private void StopIndexing()
        {
            indexRun = false;
            this.progressBar1.Value = 0;
            indexSearcher = null;
        }
        private void button1_Click(object sender, EventArgs e)
        {
            StopIndexing();   
        }

        private void SearchCriteriaTextbox_TextChanged(object sender, EventArgs e)
        {
           // Search(SearchCriteriaTextbox.Text);
        }

        private void toolStripMenuItem1_Click(object sender, EventArgs e)
        {
            this.Show();
            if (this.WindowState == FormWindowState.Minimized) this.WindowState = FormWindowState.Normal;
            this.BringToFront();
            this.Focus();

        }

        private void toolStripMenuItem2_Click(object sender, EventArgs e)
        {
            this.Hide();
        }

        private void toolStripMenuItem3_Click(object sender, EventArgs e)
        {
            StopIndexing();
            Application.Exit();
        }

        private void addToIndexToolStripMenuItem_Click(object sender, EventArgs e)
        {
            System.Windows.Forms.FolderBrowserDialog fb = new FolderBrowserDialog();
            if (fb.ShowDialog() == DialogResult.OK)
            {
                string folder = fb.SelectedPath;
                if (!folder.EndsWith(@"\")) folder += @"\";
                IndexPaths.Add(new IndexPath() { Path = folder, Indexed = true });
                IndexPaths = IndexPaths;
                //System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(Index), null);
            }
        }

        private void Form1_SizeChanged(object sender, EventArgs e)
        {
            this.Visible = (this.WindowState != FormWindowState.Minimized);
        }

        private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            toolStripMenuItem1_Click(sender, e);
        }

        private void notifyIcon1_MouseClick(object sender, MouseEventArgs e)
        {
            if (this.Visible)
                toolStripMenuItem2_Click(sender, e);
            else
                toolStripMenuItem1_Click(sender, e);
        }

        private void IndexPathGrid_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            if (System.Windows.Forms.MessageBox.Show("Are you sure you want to save these changes?", "Confirmation", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                IndexPaths = (IndexPathGrid.DataSource as List<Lucene.Net.Analyzers.IFilter.IndexPath>);
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            addToIndexToolStripMenuItem_Click(sender, e);
        }

        private void ToggleDetails_Click(object sender, EventArgs e)
        {
            splitContainer1.Panel2Collapsed = !splitContainer1.Panel2Collapsed;
        }


    }
}
